/*
 * Copyright 2013-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

package com.hzw.saas.common.security.token;

import com.hzw.saas.common.security.config.customer.SaasSecurityConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.security.oauth2.provider.authentication.TokenExtractor;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.List;

/**
 * 自定义提取token，走白名单的url，提取的token为null
 * 解决访问白名单url，请求头中带token，返回401的情况
 * @author Dave Syer
 *
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class MyBearerTokenExtractor implements TokenExtractor {

    private final SaasSecurityConfig saasSecurityConfig;

    @Override
    public Authentication extract(HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        List<String> ignoreUrls = saasSecurityConfig.ignoreAuthUrls();
        // 提取到的token
        String tokenValue = extractToken(request);
        // 在白名单中的URL，忽略token异常信息
        if(log.isDebugEnabled()) {
            log.debug("check {} contains {}", ignoreUrls, requestURI);
        }
        for(String url : ignoreUrls) {
            if(new AntPathRequestMatcher(url).matcher(request).isMatch()) {
                // 白名单的url获取不到请求头中的token
                return null;
//                // 在白名单中的URL屏蔽错误的token
//                // 检查token是否有效
//                try {
//                    OAuth2Authentication oAuth2Authentication = resourceServerTokenServices.loadAuthentication(tokenValue);
//                    if(Objects.nonNull(oAuth2Authentication)) {
//                        break;
//                    }
//                } catch (Exception e) {
//                    return null;
//                }
//                // 无效返回null
//                return null;
            }
        }

        if (tokenValue != null) {
            return new PreAuthenticatedAuthenticationToken(tokenValue, "");
        }
        return null;
    }

    protected String extractToken(HttpServletRequest request) {
        // first check the header...
        String token = extractHeaderToken(request);

        // bearer type allows a request parameter as well
        if (token == null) {
            log.debug("Token not found in headers. Trying request parameters.");
            token = request.getParameter(OAuth2AccessToken.ACCESS_TOKEN);
            if (token == null) {
                log.debug("Token not found in request parameters.  Not an OAuth2 request.");
            }
            else {
                request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, OAuth2AccessToken.BEARER_TYPE);
            }
        }

        return token;
    }

    /**
     * Extract the OAuth bearer token from a header.
     *
     * @param request The request.
     * @return The token, or null if no OAuth authorization header was supplied.
     */
    protected String extractHeaderToken(HttpServletRequest request) {
        Enumeration<String> headers = request.getHeaders("Authorization");
        while (headers.hasMoreElements()) { // typically there is only one (most servers enforce that)
            String value = headers.nextElement();
            if ((value.toLowerCase().startsWith(OAuth2AccessToken.BEARER_TYPE.toLowerCase()))) {
                String authHeaderValue = value.substring(OAuth2AccessToken.BEARER_TYPE.length()).trim();
                // Add this here for the auth details later. Would be better to change the signature of this method.
                request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE,
                    value.substring(0, OAuth2AccessToken.BEARER_TYPE.length()).trim());
                int commaIndex = authHeaderValue.indexOf(',');
                if (commaIndex > 0) {
                    authHeaderValue = authHeaderValue.substring(0, commaIndex);
                }
                return authHeaderValue;
            }
        }

        return null;
    }

}
